/*
* This file is part of ZSE Info.
*
* ZSE Info is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* any later version.
*
* ZSE Info is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Foobar; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
package org.enbyted.android.zseinfo.data.replacements;
import android.util.Log;
import org.enbyted.android.zseinfo.data.configuration.Configuration;
import org.htmlcleaner.HtmlCleaner;
import org.htmlcleaner.TagNode;
import java.io.IOException;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.ArrayList;
import java.util.List;
/**
* Created by Bartosz Grabias on 23.02.14.
*/
public class VulcanReplacementsHandler extends ReplacementsHandler {
public static final String TAG = "ZSEINFO_VULCAN_REPLACEMENTS";
@Override
public Replacements loadReplacements() throws IOException {
TagNode node = downloadReplacements();
if(node.findElementByName("head", true) != null) {
return processXHTML(node);
} else {
return processHTML(node);
}
}
// final int CELL_TEACHER = 0;
// final int CELL_TITLE = 1;
// final int CELL_HEADER = 2;
// final int CELL_LESSON = 3;
// final int CELL_DESCRIPTION = 4;
// final int CELL_REPLACEMENT = 5;
// final int CELL_REMARKS = 6;
private enum CellType {
NONE,
TITLE,
TEACHER,
HEADER,
LESSON,
DESCRIPTION,
REPLACEMENT,
REMARKS,
SEPARATOR,
}
private static TagNode downloadReplacements() throws IOException {
HtmlCleaner cleaner = new HtmlCleaner();
String url = Configuration.getInstance().getReplacementsConfig().getReplacementsUrl();
InputStream input;
HttpURLConnection connection = (HttpURLConnection) new URL(url).openConnection();
if(connection.getResponseCode() != HttpURLConnection.HTTP_OK) {
throw new IOException("Błąd połączenia z serwerem: " + connection.getResponseCode() + " " + connection.getResponseMessage());
}
input = connection.getInputStream();
TagNode node = cleaner.clean(input, Configuration.getInstance().getReplacementsConfig().getEncoding());
try {
return node.findElementByName("table", true)
.findElementByName("tbody", false);
} catch (NullPointerException e) {
if(node.findElementByName("frame", true) != null) {
TagNode frame = node.findElementByName("frame", true);
String sheetAddr = frame.getAttributeByName("src");
connection = (HttpURLConnection) new URL(url + sheetAddr).openConnection();
if(connection.getResponseCode() != HttpURLConnection.HTTP_OK) {
throw new IOException("Błąd połączenia z serwerem: " + connection.getResponseCode() + " " + connection.getResponseMessage());
}
input = connection.getInputStream();
node = cleaner.clean(input, Configuration.getInstance().getReplacementsConfig().getEncoding());
return node;//.findElementByName("table", true)
//.findElementByName("tbody", false);
}
throw e;
}
}
private Replacements processXHTML(TagNode table) {
Log.v(TAG, "Starting replacements XHTML processing");
//Due to some weird bug all TDs are in root table element
TagNode[] cells = table.getElementsByName("td", true);
final String classTeacherName = "xl69";
final String classTitle = "xl68";
List<Replacement> replacements = new ArrayList<>();
List<Replacement.Entry> entries = new ArrayList<>();
String title = "";
String teacher = "";
int lessonId = 0;
String description = "";
String replacement = "";
String remarks = "";
int headerCount = 0;
boolean replacementAdded = false;
CellType cellType = CellType.NONE;
for(TagNode cell : cells) {
// Cell type detection here
String cellClass = cell.getAttributeByName("class");
if(cellClass == null) cellClass = "";
Log.d(TAG, "Cell with class: " + cellClass);
if(cellType == CellType.SEPARATOR && ! replacementAdded) {
Log.d(TAG, "Adding replacement");
replacementAdded = true;
String[] dateParts = title.split(" ", 4);
replacements.add(new Replacement(
teacher,
entries.toArray(new Replacement.Entry[entries.size()]),
dateParts.length==4?dateParts[3]:title
));
} else if(cellType != CellType.SEPARATOR) {
replacementAdded = false;
}
switch(cellClass) {
case classTeacherName: {
Log.v(TAG, "Looks like we hit a teacher.");
cellType = CellType.TEACHER;
break;
}
case classTitle: {
Log.v(TAG, "Looks like we hit a title.");
cellType = CellType.TITLE;
break;
}
default: {
switch(cellType) {
case TEACHER: {
Log.v(TAG, "Looks like we hit a header.");
cellType = CellType.HEADER;
break;
}
case HEADER: {
headerCount++;
if(headerCount < 3) break;
headerCount = 0;
//fallthrough
}
case SEPARATOR:
case REMARKS: {
Log.v(TAG, "Looks like we hit a lesson.");
cellType = CellType.LESSON;
break;
}
case LESSON: {
Log.v(TAG, "Looks like we hit a description.");
cellType = CellType.DESCRIPTION;
break;
}
case DESCRIPTION: {
Log.v(TAG, "Looks like we hit a replacement.");
cellType = CellType.REPLACEMENT;
break;
}
case REPLACEMENT: {
Log.v(TAG, "Looks like we hit a remarks.");
cellType = CellType.REMARKS;
break;
}
default: {
Log.w(TAG, "PANIC! HTML processor is in unknown state, I can't detect cell type. Current cell type is: " + cellType.toString());
Log.w(TAG, "Assuming it's a header row, but THIS IS BUG!");
cellType = CellType.HEADER;
break;
}
}
break;
}
}
//Cell data processing here
switch(cellType) {
case TITLE: {
title = clearString(cell.getText());
break;
}
case TEACHER: {
teacher = clearString(cell.getText());
entries.clear();
break;
}
case LESSON: {
lessonId = 0;
description = "";
replacement = "";
remarks = "";
try {
lessonId = Integer.valueOf(clearString(cell.getText()));
} catch (Exception e) { //If we can't parse lesson ID then it is a separator row OR something is wrong with parser.
Log.v(TAG, "Looks like we hit a separator.", e);
cellType = CellType.SEPARATOR;
continue;
}
break;
}
case DESCRIPTION: {
description = clearString(cell.getText());
break;
}
case REPLACEMENT: {
replacement = clearString(cell.getText());
break;
}
case REMARKS: {
remarks = clearString(cell.getText());
Log.d(TAG, "Adding new entry with ID="+lessonId+", desc="+description+", repl="+replacement+"rem="+remarks);
entries.add(new Replacement.Entry(
lessonId,
description,
replacement,
remarks
));
break;
}
}
}
return new Replacements(title, replacements.toArray(new Replacement[replacements.size()]));
}
private String clearString(CharSequence input) {
String val = input.toString().trim();
return val.equals(" ")?"":val;
}
private Replacements processHTML(TagNode table) {
Log.v(TAG, "Starting replacements HTML processing");
TagNode[] rows = table.getAllElements(false);
final String classTitle = "st0";
final String classTeacher = "st1";
List<Replacement> replacements = new ArrayList<>();
List<Replacement.Entry> entries = new ArrayList<>();
String title = "";
String teacher = "";
int lessonId = 0;
String description = "";
String replacement = "";
String remarks = "";
boolean firstRow = false;
boolean replacementAdded = false;
CellType cellType = CellType.NONE;
for(TagNode rowNode : rows) {
if(! rowNode.getName().equalsIgnoreCase("tr"))
continue;
if(cellType == CellType.SEPARATOR && !replacementAdded) {
String[] dateParts = title.split(" ", 4);
replacements.add(new Replacement(
teacher,
entries.toArray(new Replacement.Entry[entries.size()]),
dateParts.length==4?dateParts[3]:title
));
entries.clear();
lessonId = 0;
description = "";
replacement = "";
remarks = "";
replacementAdded = true;
} else if(cellType != CellType.SEPARATOR) {
replacementAdded = false;
}
TagNode[] cells = rowNode.getAllElements(false);
for(TagNode cellNode : cells) {
if(! cellNode.getName().equalsIgnoreCase("td"))
continue;
String cellClass = cellNode.getAttributeByName("class");
Log.d(TAG, "Cell with class: " + cellClass);
switch(cellClass) {
case classTitle: {
Log.v(TAG, "Looks like a title cell");
cellType = CellType.TITLE;
break;
}
case classTeacher: {
Log.v(TAG, "Looks like a teacher cell");
cellType = CellType.TEACHER;
break;
}
default: {
switch(cellType) {
case SEPARATOR: {
Log.v(TAG, "Separator, skipping");
continue;
}
case TEACHER: {
Log.v(TAG, "Looks like a header cell");
cellType = CellType.HEADER;
break;
}
case REMARKS:
case HEADER: {
Log.v(TAG, "Looks like a lesson cell");
cellType = CellType.LESSON;
break;
}
case LESSON: {
Log.v(TAG, "Looks like a description cell");
cellType = CellType.DESCRIPTION;
break;
}
case DESCRIPTION: {
Log.v(TAG, "Looks like a replacement cell");
cellType = CellType.REPLACEMENT;
break;
}
case REPLACEMENT: {
Log.v(TAG, "Looks like a remarks cell");
cellType = CellType.REMARKS;
break;
}
default: {
Log.w(TAG, "PANIC! HTML processor is in unknown state, I can't detect cell type. Current cell type is: " + cellType.toString());
Log.w(TAG, "Assuming it's a header row, but THIS IS BUG!");
cellType = CellType.HEADER;
break;
}
}
break;
}
}
switch (cellType) {
case TITLE: {
title = clearString(cellNode.getText());
break;
}
case TEACHER: {
teacher = clearString(cellNode.getText());
break;
}
case LESSON: {
try {
lessonId = Integer.valueOf(clearString(cellNode.getText()));
} catch (Exception e) { //If we can't parse lesson ID then it is a separator row OR something is wrong with parser.
Log.v(TAG, "Looks like we hit a separator.", e);
cellType = CellType.SEPARATOR;
continue;
}
break;
}
case DESCRIPTION: {
description = clearString(cellNode.getText());
break;
}
case REPLACEMENT: {
replacement = clearString(cellNode.getText());
break;
}
case REMARKS: {
remarks = clearString(cellNode.getText());
Log.d(TAG, "Adding new entry with ID="+lessonId+", desc="+description+", repl="+replacement+"rem="+remarks);
entries.add(new Replacement.Entry(
lessonId,
description,
replacement,
remarks
));
break;
}
}
if( cellType == CellType.SEPARATOR
|| cellType == CellType.HEADER
) break;
}
}
if(entries.size() > 0) {
String[] dateParts = title.split(" ", 4);
replacements.add(new Replacement(
teacher,
entries.toArray(new Replacement.Entry[entries.size()]),
dateParts.length==4?dateParts[3]:title
));
}
return new Replacements(title, replacements.toArray(new Replacement[replacements.size()]));
}
@Override
public String getType() {
return "vulcan";
}
}